﻿using Abp.BackgroundJobs;
using Abp.Domain.Repositories;
using Abp.Domain.Services;
using Abp.Events.Bus;
using Abp.ObjectMapping;
using Abp.Runtime.Caching;
using Abp.Runtime.Validation;
using Abp.UI;
using AVIS.OutBound.AWD;
using AVIS.OutBound.BookOrders.Cache;
using AVIS.OutBound.CarGroups;
using AVIS.OutBound.CarGroups.Job;
using AVIS.OutBound.Orders;
using AVIS.OutBound.Orders.Cache;
using AVIS.OutBound.RateCodes;
using AVIS.OutBound.RateCodes.Cache;
using AVIS.OutBound.Wizard;
using AVIS.OutBound.WizardAgent;
using Castle.Core.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AVIS.OutBound.BookOrders
{
    /// <summary>
    /// 预订单的领域服务
    /// </summary>
    public class BookOrderManager : DomainService, IBookOrderManager
    {
        #region 构造函数
        public IEventBus EventBus { get; set; }

        private readonly ICacheManager _cacheManager;
        private readonly IObjectMapper _objectMapper;
        private readonly IAWDManager _awdManager;
        private readonly IRateCodeManager _rateCodeMananger;
        private readonly ICarGroupManager _carGroupManager;
        private readonly WizardVehRetResAgent _wizardRetAgent;
        private readonly IOrderRepository _OrderRepository;
        private readonly IBackgroundJobManager _backgroundJobManager;
        private readonly IWizardManager _wizardManager;

        public BookOrderManager(ICacheManager cacheManager,
            IAWDManager awdManager,
            IObjectMapper objectMapper,
            IRateCodeManager RateCodeMananger,
            ICarGroupManager carGroupManager,
            IBackgroundJobManager backgroundJobManager,
            IOrderRepository OrderRepository,
            IWizardManager wizardManager)
        {
            _cacheManager = cacheManager;
            _awdManager = awdManager;
            _objectMapper = objectMapper;
            _rateCodeMananger = RateCodeMananger;
            _carGroupManager = carGroupManager;
            _wizardRetAgent = new WizardVehRetResAgent();
            _OrderRepository = OrderRepository;
            _backgroundJobManager = backgroundJobManager;
            _wizardManager = wizardManager;

            EventBus = NullEventBus.Instance;
        }
        #endregion

        /// <summary>
        /// 保存预定到到缓存中
        /// </summary>
        /// <param name="order">预定单</param>
        /// <returns></returns>
        public BookOrder SaveBookOrderCache(BookOrder order)
        {
            // 1. 需要从缓存中查询是否有OrderId，有需要将缓存数据获取出来，没有则为新的订单
            var bookOrderItem = GetBookOrderCacheOrNew(order.OrderId);
            if (bookOrderItem.IsSubmit) // 判断订单是否已经提交
            {
                throw new UserFriendlyException(string.Format("订单已提交，请不要重复提交。订单号是：{0}。稍后回到首页!", bookOrderItem.OrderNo));
            }

            // 2. 将提交的订单信息合并到预定单（BookOrderItem）中
            bookOrderItem.MapFrom(order);

            // 3. 如果SIPPCode和RateCode都不为空，则表示选中了车组，需要比较价格是否已经有变化了
            if (!string.IsNullOrEmpty(order.SIPPCode) && !string.IsNullOrEmpty(order.RateCode))
            {
                // 3.1 从Wizard接口中获取指定车组的信息（订单中包含SIPPCode和RateCode）
                var carGroup = _carGroupManager.GetCarGroup(_objectMapper.Map<AgentCarEquipmentRequest>(bookOrderItem));

                // 设置订单预付总金额和币种
                bookOrderItem.CarGroup = carGroup;
                bookOrderItem.Amount = carGroup.Price;
                bookOrderItem.CurrencyCode = carGroup.CurrencyCode;
                
                // 如果价格发送变化
                if (order.Amount != carGroup.Price)
                {
                    // 3.2 触发预订单中的车组价格发生变化事件
                    EventBus.Trigger(new CarGroupPriceChangedEventData { OrderId = bookOrderItem.OrderId, RateCode = order.RateCode, SIPPCode = order.SIPPCode });

                    // 3.3 提醒用户价格变化了
                    throw new UserFriendlyException(string.Format("您选中车型组的价格已发生变化，从{0}变化到{1}，请刷新页面再试！", order.Amount, carGroup.Price));
                }
            }

            // 4. 将预定单信息保存到BookOrderItem中
            SaveBookOrderInCache(bookOrderItem);

            // 5. 触发预定单信息保存完成事件.
            EventBus.Trigger(new BookOrderSaveCompletedEventData { OrderId = bookOrderItem.OrderId });

           
            return _objectMapper.Map<BookOrder>(bookOrderItem);
        }

        /// <summary>
        /// 获取指定OrderId的预定单
        /// </summary>
        /// <param name="orderId">订单ID</param>
        /// <returns></returns>
        public BookOrder GetBookOrder(string orderId)
        {
            BookOrderCacheItem bookOrderItem = null;

            // orderId不为空时，获取指定orderId的缓存
            if (!string.IsNullOrWhiteSpace(orderId))
            {
                bookOrderItem = _cacheManager.GetBookOrderCache().GetOrDefault(orderId);
            }

            if (bookOrderItem != null)
            {
                var bookOrder= _objectMapper.Map<BookOrder>(bookOrderItem);
                if (bookOrder.CarGroup != null)
                {
                    bookOrder.RatePackage = _rateCodeMananger.GetRateCodeData(bookOrder);
                }
                return bookOrder;
            }
            else
            {
                // 则提醒用户订单过期
                throw new UserFriendlyException(string.Format("指定的订单ID（{0}）不存在，可能订单已经过期！", orderId));
            }
        }

        #region 私有方法
        /// <summary>
        /// 将预定单保存到缓存中
        /// </summary>
        /// <param name="cacheItem">预定单</param>
        private void SaveBookOrderInCache(BookOrderCacheItem cacheItem)
        {
            if (CheckVaild(cacheItem))
            {
                Logger.Debug(string.Format("更新缓存的预订单对象（{0}），JSON结构体为：{1}", cacheItem.OrderId, Newtonsoft.Json.JsonConvert.SerializeObject(cacheItem)));

                _cacheManager.GetBookOrderCache().Set(cacheItem.OrderId, cacheItem);
            }
        }

        /// <summary>
        /// 校验订单信息是否有效
        /// </summary>
        /// <param name="order"></param>
        /// <returns></returns>
        private bool CheckVaild(BookOrderCacheItem cacheItem)
        {
            // 校验预订单对象
            if (cacheItem.PickupDateTime.Ticks <= 0)
            {
                throw new UserFriendlyException("取车时间不能为空");
            }

            if (cacheItem.ReturnDateTime.Ticks <= 0)
            {
                throw new UserFriendlyException("还车时间不能为空");
            }

            if (cacheItem.RentalLength <= 0)
            {
                throw new UserFriendlyException("租期无效，请重新选择取车时间和还车时间");
            }

            if (string.IsNullOrWhiteSpace(cacheItem.PickUpCityCode) || string.IsNullOrWhiteSpace(cacheItem.PickUpStoreCode))
            {
                throw new UserFriendlyException("取车门店或城市不能同时为空");
            }

            if (string.IsNullOrWhiteSpace(cacheItem.ReturnCityCode) || string.IsNullOrWhiteSpace(cacheItem.ReturnStoreCode))
            {
                throw new UserFriendlyException("还车门店或城市不能同时为空");
            }

            return true;
        }

        /// <summary>
        /// 获取缓存中的订单对象，如果没有就创建一个空的预定单对象
        /// </summary>
        /// <param name="orderId"></param>
        /// <returns></returns>
        private BookOrderCacheItem GetBookOrderCacheOrNew(string orderId)
        {
            var cache = _cacheManager.GetBookOrderCache();
            BookOrderCacheItem bookOrderItem = null;
            if (string.IsNullOrWhiteSpace(orderId))
            {
                bookOrderItem = new BookOrderCacheItem
                {
                    OrderId = Guid.NewGuid().ToString(),
                    BookingDate = Abp.Timing.Clock.Now,
                };
            }
            else
            {
                // 获取指定orderId的缓存数据
                bookOrderItem = cache.Get(orderId,
                    (key) =>
                    {
                        return new BookOrderCacheItem
                        {
                            OrderId = Guid.NewGuid().ToString(),
                            BookingDate = Abp.Timing.Clock.Now,
                        };
                    });
            }

            return bookOrderItem;
        }
        #endregion
    }
}
